package com.accesshollywood.GL;

import java.util.ArrayList;
import java.util.LinkedList;
import static org.lwjgl.opengl.GL11.*;

import org.lwjgl.util.vector.Vector3f;
import org.newdawn.slick.opengl.Texture;

public abstract class AnimObject implements RenderGLable {
	public static final float		TWO_PI					= (float) (2 * Math.PI);
	public static final float		BOUNCE					= 0.8f;
	public static final float		INV_BOUNCE				= 1.0f / BOUNCE;
	public static final boolean		JUMP_TO_LAST_KEYFRAME	= false;

	protected Texture				texture;

	protected LinkedList<Keyframe>	keyframes;
	protected Keyframe				currKeyframe;

	public Vector3f					posCurr;
	protected Vector3f				rotCurr;
	protected Vector3f				scaleCurr;
	protected float					alphaCurr;

	protected Vector3f				posStart;
	protected Vector3f				rotStart;
	protected Vector3f				scaleStart;
	protected float					alphaStart;

	protected Vector3f				posGoal;
	protected Vector3f				rotGoal;
	protected Vector3f				scaleGoal;
	protected float					alphaGoal;

	protected int					currFrame;
	public float					scaleBase				= 1.0f;

	public boolean					isBusy;
	protected boolean				isPolite;
	protected float					tweenRatio;

	public AnimObject				parent					= null;
	public ArrayList<AnimObject>	children				= new ArrayList<AnimObject>();

	public AnimObject() {
		keyframes = new LinkedList<Keyframe>();
		isBusy = false;
		setCurrents();
	}

	public boolean hasKeyframes() {
		return (keyframes.size() > 0);
	}

	public void addKeyframe(Keyframe newKeyframe) {
		keyframes.add(newKeyframe);
	}

	public Keyframe getNextKeyframe() {
		return keyframes.poll();
	}

	public void setCurrents() {
		posCurr = new Vector3f(0.0f, 0.0f, 0.0f);
		rotCurr = new Vector3f(0.0f, 0.0f, 0.0f);
		scaleCurr = new Vector3f(1.0f, 1.0f, 1.0f);
		alphaCurr = 1.0f;
		currFrame = 0;
	}

	public void setStarts() {
		posStart = new Vector3f(posCurr.x, posCurr.y, posCurr.z);
		rotStart = new Vector3f(rotCurr.x, rotCurr.y, rotCurr.z);
		scaleStart = new Vector3f(scaleCurr.x, scaleCurr.y, scaleCurr.z);
		alphaStart = alphaCurr;
		currFrame = 0;
	}

	public void setGoals() {
		if (currKeyframe.getPosXYZ() != null) {
			posGoal = currKeyframe.getPosXYZ();
		} else {
			posGoal = new Vector3f(posCurr.x, posCurr.y, posCurr.z);
		}
		if (currKeyframe.getRotXYZ() != null) {
			rotGoal = currKeyframe.getRotXYZ();
		} else {
			rotGoal = new Vector3f(rotCurr.x, rotCurr.y, rotCurr.z);
		}
		if (currKeyframe.getScaleXYZ() != null) {
			scaleGoal = currKeyframe.getScaleXYZ();
		} else {
			scaleGoal = new Vector3f(scaleCurr.x, scaleCurr.y, scaleCurr.z);
		}
		if (currKeyframe.getAlpha() != -1) {
			alphaGoal = currKeyframe.getAlpha();
		} else {
			alphaGoal = alphaCurr;
		}
	}

	public void jumpToKeyframe() {
		posCurr = currKeyframe.getPosXYZ();
		rotCurr = currKeyframe.getRotXYZ();
		scaleCurr = currKeyframe.getScaleXYZ();
		alphaCurr = currKeyframe.getAlpha();
	}

	public void processNextKeyframe(boolean isPolite) {
		this.isPolite = isPolite;
		if (isPolite) {
			// addEventListener(Event.ENTER_FRAME,bePolite);
		} else {
			if (JUMP_TO_LAST_KEYFRAME && currKeyframe != null) {
				jumpToKeyframe(); // make sure we're done animating from the
									// last keyframe -- may not want this
									// actually
			}
			currKeyframe = getNextKeyframe();
			setStarts();
			setGoals();
			isBusy = true;
			// addEventListener(Event.ENTER_FRAME, animateFrame);
		}
	}

	public void bePolite() {
		if (!isBusy) {
			// removeEventListener(Event.ENTER_FRAME, bePolite);
			processNextKeyframe(!Keyframe.POLITE_KEYFRAME);
		}
	}

	public void animateFrame(int delta) {
		if (isPolite) {
			if (!isBusy) {
				processNextKeyframe(!Keyframe.POLITE_KEYFRAME);
			}
		} else {
			currFrame += delta;
			tweenRatio = currFrame / (float) currKeyframe.getFrames();
			float commonFunc = (float) (TWO_PI * (-0.25 + 1.25 * tweenRatio));

			// float tweenRatioAdj = (float) Math.min(1.0f,(INV_BOUNCE *
			// tweenRatio + BOUNCE * (commonFunc - Math.cos(commonFunc) -
			// Math.sin(commonFunc) + 0.57) / 5.85319)/2.0f);
			float tweenRatioAdj = (float) (INV_BOUNCE * tweenRatio + BOUNCE * (commonFunc - Math.cos(commonFunc) - Math.sin(commonFunc) + 0.572279536679665) / 5.934372) / 2.0393627f;

			// System.out.println(Math.cos(commonFunc)+"   "+Math.sin(commonFunc)+"   "+(commonFunc
			// - Math.cos(commonFunc) - Math.sin(commonFunc) +
			// 0.572279536679665));
			// System.out.println(currFrame + "   " +
			// tweenRatio+"   "+commonFunc+"   "+tweenRatioAdj);

			posCurr.x = posStart.x + (posGoal.x - posStart.x) * tweenRatioAdj;
			posCurr.y = posStart.y + (posGoal.y - posStart.y) * tweenRatioAdj;
			posCurr.z = posStart.z + (posGoal.z - posStart.z) * tweenRatioAdj;
			rotCurr.x = rotStart.x + (rotGoal.x - rotStart.x) * tweenRatioAdj;
			rotCurr.y = rotStart.y + (rotGoal.y - rotStart.y) * tweenRatioAdj;
			rotCurr.z = rotStart.z + (rotGoal.z - rotStart.z) * tweenRatioAdj;
			scaleCurr.x = Math.max(0, scaleStart.x + (scaleGoal.x - scaleStart.x) * tweenRatioAdj);
			scaleCurr.y = Math.max(0, scaleStart.y + (scaleGoal.y - scaleStart.y) * tweenRatioAdj);
			scaleCurr.z = Math.max(0, scaleStart.z + (scaleGoal.z - scaleStart.z) * tweenRatioAdj);
			alphaCurr = Math.max(0, alphaStart + (alphaGoal - alphaStart) * tweenRatioAdj);
			if (currFrame >= currKeyframe.getFrames()) {
				isBusy = false;
				posCurr.x = posGoal.x;
				posCurr.y = posGoal.y;
				posCurr.z = posGoal.z;
				rotCurr.x = rotGoal.x;
				rotCurr.y = rotGoal.y;
				rotCurr.z = rotGoal.z;
				scaleCurr.x = Math.max(0, scaleGoal.x);
				scaleCurr.y = Math.max(0, scaleGoal.y);
				scaleCurr.z = Math.max(0, scaleGoal.z);
				alphaCurr = Math.max(0, alphaGoal);
				// if(currKeyframe.callback != null){
				// currKeyframe.callback();
				// }
				// currKeyframe = null;
				// removeEventListener(Event.ENTER_FRAME, animateFrame);
				if (keyframes.size() > 0) {
					processNextKeyframe(!Keyframe.POLITE_KEYFRAME);
				} else {
					currKeyframe = null;
				}
			}
		}
	}

	public void update(int delta) {
		for (int i = 0; i < children.size(); i++) {
			AnimObject currObject = children.get(i);
			// if (currObject.isBusy || currObject.hasKeyframes()) {
			currObject.update(delta);
			// }
		}
		if (isBusy || hasKeyframes()) {
			animateFrame(delta);
		}
	}

	public void render() {
		glPushMatrix();
		renderGL();
		for (int i = 0; i < children.size(); i++) {
			AnimObject currObject = children.get(i);
			currObject.render();
		}
		glPopMatrix();
	}

	public void renderGL() {
		glTranslatef(posCurr.x, posCurr.y, posCurr.z);
		glRotatef(rotCurr.x, 1f, 0f, 0f);
		glRotatef(rotCurr.y, 0f, 1f, 0f);
		glRotatef(rotCurr.z, 0f, 0f, 1f);
		glScalef(scaleCurr.x, scaleCurr.y, scaleCurr.z);
	}

	public void swapParents(AnimObject currParent, AnimObject newParent) {
		// transform.matrix3D = transform.getRelativeMatrix3D(newParent);

		// newParent.addChild(this);
	}

	public float getWidth() {
		System.out.println("IMPLEMENT ANIMOBJECT.GETWIDTH");
		return 0;
	}

	public float getHeight() {
		System.out.println("IMPLEMENT ANIMOBJECT.GETHEIGHT");
		return 0;
	}
}
